use chrono::Local;
use opencv::{
  core::{bitwise_and, count_non_zero, in_range, Mat, MatTraitConst, Rect},
  imgcodecs,
  imgproc::{threshold, THRESH_BINARY},
};
use rand::Rng;

use super::{BaseFitneser, Fitneser};
use crate::{
  cv::Cv,
  fitness::options,
  window::{UIState, Window},
};
use std::{
  cmp,
  error::Error,
  sync::{mpsc::Sender, Arc, Mutex},
};

pub struct WheelBarrow {
  _super: BaseFitneser,
  workspace: Rect,
  temp_drive: Mat,
  temp_exc: Mat,
}

impl WheelBarrow {
  pub fn new(mutex: Arc<Mutex<bool>>) -> Self {
    WheelBarrow {
      _super: BaseFitneser::new(mutex),
      workspace: Rect::new(330 - 8, 70, 630, 720 - 70),
      temp_drive: Mat::default(),
      temp_exc: Mat::default(),
    }
  }
}

impl Fitneser for WheelBarrow {
  fn init(&mut self, opts: &options::Options, assets_path: String) -> Result<bool, Box<dyn Error>> {
    self._super.setup(opts, assets_path.clone());
    // 读取驾驶判断模板
    let mut tmp_str = String::from(assets_path.clone());
    tmp_str += "/temp_drive.jpg";
    self.temp_drive = imgcodecs::imread(&tmp_str, imgcodecs::IMREAD_GRAYSCALE)?;
    tmp_str = String::from(assets_path.clone());
    tmp_str += "/temp_exc.jpg";
    self.temp_exc = imgcodecs::imread(&tmp_str, imgcodecs::IMREAD_GRAYSCALE)?;
    Ok(true)
  }

  /// 运行练体
  /// ### 返回值
  /// * `true` 外部正常结束
  /// * `false` 异常结束
  fn run(&mut self, win: &Window, msg_tx: &Sender<String>) -> Result<bool, Box<dyn Error>> {
    // 开始推车
    // 重置状态
    if self.is_driving(win)? {
      self.stop(win)?;
    }
    let mut _state = 0;
    // 是否使用特殊方案
    let mut _ext_driver = false;
    'outter: loop {
      if !self._super.is_running() {
        break;
      }
      // if self._super.exit_no_die(win)? {
      //   let _ = msg_tx.send(String::from("生命值偏低，暂时退出"));
      //   return Ok(false);
      // }
      // 检查掉线
      if self._super.check_lost(win)? {
        self.stop(win)?;
        if !self._super.handle_lost(win)? {
          self._super.stop_running();
          let _ = msg_tx.send(format!("掉线无法重连"));
          return Ok(false);
        }
        continue;
      }
      if _state == 0 {
        _state = self._super.fetch_state(win)?;
      }
      // 放下箱子并吃东西
      if _state != 0 {
        let _ = msg_tx.send(format!("当前状态: {}", _state));
        // 如果需要退出 先捡起包 免得丢失
        if _state & (1 << 8) == 256 {
          self.stop(win)?;
          self._super.exit_to_lobby(win)?;
          return Ok(false);
        }
        if !self._super.ensure_base(win, _state)? {
          break;
        }
        // 温度过高
        if _state & 16 == 16 {
          let _ = msg_tx.send(format!("温度过高 冷静 1 分钟"));

          for _ in 0..60 {
            win.sleep(1000);
            if !self._super.is_running() {
              break 'outter;
            }
          }
        }
      }
      // 推车
      if _ext_driver {
        let (success, is_pushed) = self.drive_ext(win)?;
        if is_pushed {
          _ext_driver = false;
        }
        if !success {
          let _ = msg_tx.send(String::from("特殊推车失败 1秒后重试"));
          _state = 0;
          win.sleep(1 * 1000);
          continue;
        }
      } else if !self.drive(win)? {
        // 推车失败 尝试特殊方案
        let _ = msg_tx.send(String::from("推车失败 尝试特殊方案"));
        _ext_driver = true;
        _state = 0;
        continue;
      }
      _ext_driver = false;
      loop {
        if !self._super.is_running() {
          break 'outter;
        }
        let mut times = 0;
        loop {
          win.sleep(1000);
          times += 1000;
          if times > 1000 * 60 * 2 - 1 {
            break;
          }
          // 如果检测到停止则退出
          if !self._super.is_running() {
            break 'outter;
          }
        }
        if self._super.is_needs_repair() {
          self.stop(win)?;
          self._super.try_repair(win)?;
          _state = 0;
          break;
        }
        if !self.is_driving(win)? {
          self.reset(win)?;
          _state = 0;
          break;
        }
        _state = self._super.fetch_state(win)?;
        if _state == 0 {
          self._super.look_status(win)?;
          continue;
        }
        self.stop(win)?;
        break;
      }
    }
    if self.is_driving(win)? {
      self.stop(win)?;
    }
    Ok(true)
  }
}

impl WheelBarrow {
  fn reset(&self, win: &Window) -> Result<(), Box<dyn Error>> {
    win.ensure_ui(UIState::Default, 0)?;
    win.release_key("s")?;
    win.release_key("a")?;
    Ok(())
  }

  fn drive(&self, win: &Window) -> Result<bool, Box<dyn Error>> {
    let fmt = "%Y-%m-%d %H:%M:%S";
    let now = Local::now().format(fmt);
    println!("尝试开始锻炼: {}", now);
    let rows = ((self.workspace.height - 65) / 35) as i32 + 1;
    let cols = ((self.workspace.width - 50) / 25) as i32 + 1;
    for _ in 0..6 {
      // ensure tab 4
      win.ensure_ui(UIState::Tab, 4)?;
      let mut _y = self.workspace.y + self.workspace.height - 25;
      for _ in 0..rows {
        let mut _x = self.workspace.x + self.workspace.width - 25;
        for _ in 0..cols {
          if !self._super.is_running() {
            return Ok(false);
          }
          win.mouse_move_to(_x, _y)?;
          win.sleep(15);
          win.mouse_click_r()?;
          win.sleep(55);
          if win.is_active(_x, _y) {
            // println!("active");
            win.mouse_move(-18, 0)?;
            win.sleep(40);
            // 截图
            let gray = win.capture()?;
            let roi = gray.roi(Rect::new(
              _x - 8,
              cmp::max(0, _y - 150),
              cmp::min(60, gray.cols() - (_x - 8)),
              cmp::min(300, gray.rows() - cmp::max(0, _y - 150)),
            ))?;
            let (is_match, loc) = Cv::is_match(&roi, &self.temp_drive)?;
            if is_match {
              win.mouse_move_to(_x + 8, cmp::max(0, _y - 150) + loc.y + 5)?;
              win.sleep(125);
              win.mouse_click_l()?;
              win.sleep(2000);
              win.mouse_move(0, 1500)?;
              win.sleep(250);
              win.mouse_move(0, -450)?;
              win.press_key("a")?;
              win.press_key("s")?;
              // 打开状态界面
              self._super.look_status(win)?;
              return Ok(true);
            }
          }
          _x -= 25;
        }
        _y -= 35;
      }
      // look around
      self.look_around(win)?;
    }
    Ok(false)
  }

  /// 特殊推车 往前移动并尝试上车
  /// ### 返回值
  /// * 第一个 `bool` 代表是否成功
  /// * 第二个 `bool` 代表车是否移动
  fn drive_ext(&self, win: &Window) -> Result<(bool, bool), Box<dyn Error>> {
    let fmt = "%Y-%m-%d %H:%M:%S";
    let now = Local::now().format(fmt);
    println!("尝试特殊推车: {}", now);
    // 向前移动
    let mut is_pushd = false;
    for _ in 0..4 {
      if is_pushd {
        break;
      }
      win.ensure_ui(UIState::Tab, 4)?;
      let rows = ((self.workspace.height - 60) / 50) as i32 + 1;
      let cols = ((self.workspace.width - 50) / 30) as i32 + 1;
      let mut _y = 35;
      for _ in 0..rows {
        if is_pushd {
          break;
        }
        let mut _x = self.workspace.x + self.workspace.width - 25;
        for _ in 0..cols {
          if !self._super.is_running() {
            return Ok((false, false));
          }
          win.mouse_move_to(_x, _y)?;
          win.sleep(15);
          win.mouse_click_r()?;
          win.sleep(55);
          if win.is_active(_x, _y) {
            // println!("active");
            win.mouse_move(-18, 0)?;
            win.sleep(40);
            // 截图寻找向前轻移
            let gray = win.capture()?;
            let roi = gray.roi(Rect::new(
              _x - 8,
              cmp::max(0, _y - 120),
              cmp::min(55, gray.cols() - (_x - 8)),
              cmp::min(240, gray.rows() - cmp::max(0, _y - 120)),
            ))?;
            let (is_match, loc) = Cv::is_match(&roi, &self.temp_exc)?;
            if is_match {
              let px = _x - 8 + loc.x + 20;
              let py = cmp::max(0, _y - 120) + loc.y + 5;
              win.mouse_move_to(px, py)?;
              win.mouse_click_l()?;
              win.sleep(1000);
              // 车已经移动了 接下来需要往车跟前走。。。
              is_pushd = true;
              break;
            }
          }
          _x -= 30;
        }
        _y += 50;
      }
    }
    // 尝试推车
    win.ensure_ui(UIState::Default, 0)?;
    win.mouse_click_r()?;
    win.mouse_move(0, 1500)?;
    win.sleep(250);
    win.mouse_move(0, -750)?;
    win.sleep(50);
    for _ in 0..10 {
      // 寻找 推车
      win.ensure_ui(UIState::Tab, 4)?;
      let rows = ((self.workspace.height - 60) / 50) as i32 + 1;
      let cols = ((self.workspace.width - 50) / 30) as i32 + 1;
      let mut _y = 35;
      for _ in 0..rows {
        let mut _x = self.workspace.x + self.workspace.width - 25;
        for _ in 0..cols {
          if !self._super.is_running() {
            return Ok((false, is_pushd));
          }
          win.mouse_move_to(_x, _y)?;
          win.sleep(15);
          win.mouse_click_r()?;
          win.sleep(55);
          if win.is_active(_x, _y) {
            // println!("active");
            win.mouse_move(-18, 0)?;
            win.sleep(40);
            // 截图
            let gray = win.capture()?;
            let roi = gray.roi(Rect::new(
              _x - 8,
              cmp::max(0, _y - 150),
              cmp::min(60, gray.cols() - (_x - 8)),
              cmp::min(300, gray.rows() - cmp::max(0, _y - 150)),
            ))?;
            let (is_match, loc) = Cv::is_match(&roi, &self.temp_drive)?;
            if is_match {
              win.mouse_move_to(_x + 8, cmp::max(0, _y - 150) + loc.y + 5)?;
              win.sleep(125);
              win.mouse_click_l()?;
              win.sleep(2000);
              win.mouse_move(0, 1500)?;
              win.sleep(250);
              win.mouse_move(0, -450)?;
              win.press_key("a")?;
              win.press_key("s")?;
              // 打开状态界面
              self._super.look_status(win)?;
              return Ok((true, is_pushd));
            }
          }
          _x -= 30;
        }
        _y += 50;
      }
      // 调整视角
      win.ensure_ui(UIState::Default, 0)?;
      win.sleep(50);
      win.mouse_move(950, 0)?;
    }
    Ok((false, is_pushd))
  }

  /// 停车
  fn stop(&self, win: &Window) -> Result<(), Box<dyn Error>> {
    println!("尝试停止锻炼");
    // 强制停止所有动作
    win.release_key("ctrl")?;
    win.tap_key("esc")?;
    win.ensure_ui(UIState::Default, 0)?;
    win.release_key("s")?;
    win.release_key("a")?;
    // win.tap_key("w")?;
    // 切换到tab页面按f可以确保取消推车
    win.ensure_ui(UIState::Tab, 1)?;
    win.tap_key("f")?;
    win.sleep(1800);
    Ok(())
  }

  /// 是否正在推车
  fn is_driving(&self, win: &Window) -> Result<bool, Box<dyn Error>> {
    win.ensure_ui(UIState::Default, 0)?;
    let gray = win.capture()?;
    let roi = gray.roi(Rect::new(1150, 650, 130, 60))?;
    let mut thres = Mat::default();
    threshold(&roi, &mut thres, 200.0, 255.0, THRESH_BINARY)?;
    let mut mask = Mat::default();
    in_range(&thres, &[200], &[255], &mut mask)?;
    let mut output = Mat::default();
    bitwise_and(&thres, &thres, &mut output, &mask)?;
    let pixel = count_non_zero(&output)?;
    Ok(pixel > 1000)
  }

  /// 随机在周围找
  fn look_around(&self, win: &Window) -> Result<(), Box<dyn Error>> {
    println!("尝试转动");
    // ensure ui
    win.ensure_ui(UIState::Default, 0)?;
    win.mouse_click_r()?;
    win.mouse_move(0, 1500)?;
    win.sleep(250);
    win.mouse_move(0, -650)?;
    win.sleep(50);
    win.mouse_move(rand::thread_rng().gen_range(350, 850), 0)?;
    win.sleep(50);
    Ok(())
  }
}
